/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package MelbourneMap;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;


/**
 * <p>This class consists of static objects that control the file management and
 * the list of map.
 * This class is a main class that runs Melbourne Map including entire frame.
 * This class contains several other classes that consist the frame.</p>
 * 
 * @author Heong Jin Yu, 7019777
 * @author David Zappia, 7183216
 * @version 1.0
 */
public class MelbourneMap extends JFrame implements ItemListener, ChangeListener
{
	static final long serialVersionUID = 1L;
    public final int DEFAULT_WIDTH = 1200;
    public final int DEFAULT_HEIGHT = 950;
    private final static String PROPERTIES_FILE = "././data/default.properties";	
    private Properties Properties;
    
    //Panel for Button navigation
  	private DrawManager dm;
  	private JDesktopPane jDPanel;
    private JInternalFrame inner1;
    private JInternalFrame inner2;
    private JInternalFrame inner3;
  	
    //Dialog for adding user points
    private pointDialog pD;
    private ArrayList<pointLabel> pointList;
    
    //HANDLERS
    private MenuHandler mH;
    private CheckboxHandler cH;
    private ButtonHandler bH;
    //private MouseHandler msH;
    
    //MENU
    private JMenuItem fileSave;
    private JMenu saveAsMenu;
    private JMenuItem saveAsVictoria;
    private JMenuItem saveAsSuburb;
    private JMenuItem saveAsRoad;
    private JMenuItem saveAsRail;
    private JMenuItem saveAsOverlay;
    private JMenu loadMenu;
    private JMenuItem loadVictoria;
    private JMenuItem loadSuburb;
    private JMenuItem loadRoad;
    private JMenuItem loadRail;
    private JMenuItem loadOverlay;
    private JMenuItem fileExit;
    private JMenuItem helpAbout;
    private JMenuItem dispRail;
    private JMenuItem dispRoad;
    private JMenuItem dispSuburb;
    private JMenuItem dispOverlay;
    private JFileChooser fChooser;
    
    //Navigation Panel Components
    private JButton leftBtn;
	private JButton rightBtn;
	private JButton upBtn;
	private JButton downBtn;
	private JButton resetBtn;
	private JButton zoomInBtn;
	private JButton zoomOutBtn;
	public JSlider slider;
	
	//Control Panel Components
	private JComboBox regionCombo;
	public JRadioButton suburbJR;
	public JRadioButton roadJR;
	public JRadioButton railJR;
	private JButton colorButton;
	public JCheckBox editCheck;
	public JCheckBox addCheck;
	
	//Path Panel Components
	public JCheckBox pathCheck;
	public JButton goButton;
	private ButtonGroup vehicleGroup;
	private JRadioButton carButton;
	private JRadioButton truckButton;
	private JRadioButton trainButton;
	public JRadioButton helicopterButton;
	private JRadioButton planPathButton;
	private ButtonGroup searchGroup;
	private JRadioButton SPFButton;
	private JRadioButton aStarButton;
	
	//Data tracking variables
	private Map<String, Map<String, Region>> regionMap;
	private FileIO File = new FileIO();
	private Graph roadGraph;
	private Graph railGraph;
	private ArrayList<Vehicle> vehicles = new ArrayList<Vehicle>();
	
	//Editing tracking variables
	private String currentOverlay;
	public Region selectedReg = null;
	private boolean changedState = false;
	
	/**
	 * Class constructor
	 */
	public MelbourneMap()
	{
		pointList = new ArrayList<pointLabel>();
		
		//Initializing Handlers
		bH = new ButtonHandler();
		mH = new MenuHandler();
		cH = new CheckboxHandler();
		fChooser = new JFileChooser("././data");
		fChooser.setFileFilter(new FileNameExtensionFilter("Data Files","txt")); 
        fChooser.setMultiSelectionEnabled(false);//No multiple file select
		
		try
		{
			Properties = new Properties();
			Properties.load(new FileInputStream(PROPERTIES_FILE));
			regionMap = File.loadData(Properties);
			roadGraph = File.getRoadGraph();
			railGraph = File.getRailGraph();
			currentOverlay = "overlay";
			
			if (!regionMap.containsKey("Overlay"))
			{
				Map<String, Region> o = new HashMap<String, Region>();
				regionMap.put("Overlay", o);
			}
			
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
		
		initFrame();	

		drawPoint(); // Initiates all overlay points
		
	}
	
	public static void main(String[] args) 
	{
		new MelbourneMap();
	}

	/**
	 * Initializes the frame with components to include JMenu and JPanel.
	 * Both Menu and Panel are initialised before added. The panel is a navigation
	 * button on the map.
	 * Component Listener is added to resize and relocate the Button panel and another
	 * panel for map when the frame is resized 
	 */
	private void initFrame()
	{									
		setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
		setTitle("MelbourneMap");
		
		//adding JMenus
		setJMenuBar(initMenu());

		//Add drawmanager
		dm = new DrawManager(this, roadGraph, railGraph);
		dm.addRegion(0, regionMap.get("Victoria"));
		dm.addRegion(4, regionMap.get("Overlay"));
		dm.updateDraw();
		 
		//jDP
		jDPanel = new JDesktopPane();
		
		//title, resizing, closing, max, icon
		inner1 = new JInternalFrame("1", true, false, true, false);
		inner2 = new JInternalFrame("2", true, false, false, false);
		inner3 = new JInternalFrame("Control Frame", false, false, false, false);
		
		inner1.add(dm.getDrawPanel());
		inner1.setSize(900,900);
		inner1.setLocation(0,0);
		inner1.setVisible(true);
		
		inner2.add(dm.getViewPanel());
		inner2.setSize(300,200);
		inner2.setLocation(900,700);
		inner2.setVisible(true);
		
		inner3.add(initCtrlPanel());
		inner3.setSize(300,700);
		inner3.setLocation(900,0);
		inner3.setVisible(true);
		
		jDPanel.add(inner1);
		jDPanel.add(inner2);
		jDPanel.add(inner3);
		
		getContentPane().add(jDPanel);
		
		//Adding Listener for resizing
		this.addComponentListener(new ComponentListener()
		{
			public void componentResized(ComponentEvent evt)
			{
				dm.renewAT();
				renewPoint();
			}
			public void componentHidden(ComponentEvent arg0)
			{
			}
			public void componentMoved(ComponentEvent arg0) 
			{
			}
			public void componentShown(ComponentEvent arg0) 
			{
			}
		});
		
		addWindowListener(new WindowAdapter() 
		{
			public void windowClosing(WindowEvent e) 
			{
				if(getChangedState()) // check if there's change in overlay
				{
					doSave();
				}
				setVisible(false);
				dispose();
				System.exit(0);
			}
		});
		this.setVisible(true);
	}
	
	/**
	 * Builds up a panel with all panels including region panel, navigation panel and path panel 
	 * All buttons in the panel must be listened and do its role when clicked. 
	 * @return A panel that consists of control components
	 */
    private JPanel initCtrlPanel()
    {
    	//Panel that covers following components
    	JPanel ctrlPanel = new JPanel();
    	ctrlPanel.setLayout(new FlowLayout());
    	//ctrlPanel.setBorder(BorderFactory.createTitledBorder("Control Panel Demo"));
    	JPanel regionPanel = new JPanel();
    	JPanel navPanel = new JPanel();
    	JPanel pathPanel = new JPanel();
    	
    	//Building regionPanel
    	
    	regionPanel.setLayout(new FlowLayout());
    	regionPanel.setPreferredSize(new Dimension(260, 160));
    	regionPanel.setBorder(BorderFactory.createTitledBorder("Region Panel"));
    	
    	suburbJR = new JRadioButton("Suburb");
    	roadJR = new JRadioButton("Road");
    	railJR = new JRadioButton("Rail");
    	suburbJR.addActionListener(bH);
    	roadJR.addActionListener(bH);
    	railJR.addActionListener(bH);
    	
    	ButtonGroup gb = new ButtonGroup();
    	gb.add(suburbJR);
    	suburbJR.setSelected(true);
    	gb.add(roadJR);
    	gb.add(railJR);
    	
    	regionPanel.add(suburbJR);
    	regionPanel.add(roadJR);
    	regionPanel.add(railJR);
    	
    	regionCombo = new JComboBox(); 
    	regionCombo.setPreferredSize(new Dimension(200, 25));
    	initCombo("Suburb");
    	regionCombo.addItemListener(this);
    	regionPanel.add(regionCombo);
    	
    	editCheck = new JCheckBox();
		editCheck.setText("Edit");
		editCheck.setEnabled(false);
		editCheck.addActionListener(new ActionListener()
    	{
    		public void actionPerformed(ActionEvent e)
    		{
    			if(editCheck.isSelected())
    				addCheck.setEnabled(true);
    			else
    				addCheck.setEnabled(false);
    		}
    	});
		regionPanel.add(editCheck);
    	
		addCheck = new JCheckBox();
		addCheck.setText("Add");
		addCheck.setEnabled(false);
		regionPanel.add(addCheck);
		
    	colorButton = new JButton("Choose a color");
    	colorButton.addActionListener(new ActionListener()
    	{
    		public void actionPerformed(ActionEvent e)
    		{
    			Color c = JColorChooser.showDialog(MelbourneMap.this, "Choose a color", getBackground());
    			if(c != null)
    			{
    				Region tmpReg;
    				
    				//Returns the selected region
    				tmpReg = regionMap.get("Suburb").get(regionCombo.getSelectedItem().toString());
    				
    				tmpReg.setColor(c);
    				dm.updateDraw();
    			}
    		}
    	});
    	regionPanel.add(colorButton);
    	
    	ctrlPanel.add(regionPanel);
    	
    	//Building navPane
    	leftBtn = new JButton("Left");
    	leftBtn.addActionListener(bH);
    	rightBtn = new JButton("Right");
    	rightBtn.addActionListener(bH);
    	upBtn = new JButton("Up");
    	upBtn.addActionListener(bH);
    	downBtn = new JButton("Down");
    	downBtn.addActionListener(bH);
    	resetBtn = new JButton("Reset");
    	resetBtn.addActionListener(bH);
    	zoomInBtn = new JButton("+");
    	zoomInBtn.addActionListener(bH);
    	zoomOutBtn = new JButton("-");
    	zoomOutBtn.addActionListener(bH);
    	
    	slider = new JSlider();
    	slider.setMaximum(200);
    	slider.setMajorTickSpacing(40);
        slider.setMinorTickSpacing(10);
        slider.setValue(0);
        //slider.setSnapToTicks(true);
        slider.setPaintTicks(true);
        slider.setPaintLabels(true);
        slider.addChangeListener(this);
    	
		navPanel.setLayout(new GridBagLayout());
		((GridBagLayout)navPanel.getLayout()).columnWidths = new int[] {75, 40, 40, 70, 0};
		((GridBagLayout)navPanel.getLayout()).rowHeights = new int[] {35, 35, 35, 40, 0};
		navPanel.setPreferredSize(new Dimension(260, 230));
		navPanel.setBorder(BorderFactory.createTitledBorder("Navigate Panel"));
		
		navPanel.add(upBtn, new GridBagConstraints(1, 0, 2, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 5, 5), 0, 0));
		navPanel.add(leftBtn, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 5, 5), 0, 0));
		navPanel.add(resetBtn, new GridBagConstraints(1, 1, 2, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 5, 5), 0, 0));
		navPanel.add(rightBtn, new GridBagConstraints(3, 1, 1, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 5, 0), 0, 0));
		navPanel.add(downBtn, new GridBagConstraints(1, 2, 2, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 5, 5), 0, 0));
		navPanel.add(zoomOutBtn, new GridBagConstraints(0, 3, 2, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 0, 5), 0, 0));
		navPanel.add(zoomInBtn, new GridBagConstraints(2, 3, 2, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 0, 0), 0, 0));
		navPanel.add(slider, new GridBagConstraints(0, 4, 4, 1, 0.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(0, 0, 0, 0), 0, 0));

		ctrlPanel.add(navPanel);
		
		//Path Panel
		pathPanel.setLayout(new FlowLayout());
    	pathPanel.setPreferredSize(new Dimension(260, 240));
    	pathPanel.setBorder(BorderFactory.createTitledBorder("Path Panel"));
    	
    	pathCheck = new JCheckBox();
		pathCheck.setText("Enable path plan");
		pathCheck.setEnabled(true);
		pathCheck.addActionListener(new ActionListener()
    	{
    		public void actionPerformed(ActionEvent e)
    		{
    			if(!pathCheck.isSelected())
    			{
	    			goButton.setEnabled(false);
	    			carButton.setEnabled(false);
	    			truckButton.setEnabled(false);
	    			trainButton.setEnabled(false);
	    			helicopterButton.setEnabled(false);
	    			planPathButton.setEnabled(false);
	    			SPFButton.setEnabled(false);
	    			aStarButton.setEnabled(false);
	    		//	for (Entry<String, Region> entry : regionMap.get("Road").entrySet() )
					//	entry.getValue().selected = false;
    			}
    			else
    			{
	    			carButton.setEnabled(true);
	    			truckButton.setEnabled(true);
	    			trainButton.setEnabled(true);
	    			helicopterButton.setEnabled(true);
	    			planPathButton.setEnabled(true);
	    			SPFButton.setEnabled(true);
	    			aStarButton.setEnabled(true);
	    			//for (Entry<String, Region> entry : regionMap.get("Road").entrySet() )
					//	entry.getValue().selected = true;
    			}
    				
    		}
    	});
		
		pathPanel.add(pathCheck);
		pathPanel.add(new JLabel("                         "));
	
    	goButton = new JButton("Go");
    	goButton.addActionListener(bH);	
    	carButton = new JRadioButton("Car");
    	carButton.addActionListener(bH);
    	carButton.setSelected(true);
    	truckButton = new JRadioButton("Truck");
    	truckButton.addActionListener(bH);
    	trainButton = new JRadioButton("Train");
    	trainButton.addActionListener(bH);
    	helicopterButton = new JRadioButton("Helicopter");
    	helicopterButton.addActionListener(bH);
    	planPathButton = new JRadioButton("Path");
    	planPathButton.addActionListener(bH);
    	
    	vehicleGroup = new ButtonGroup();
    	vehicleGroup.add(carButton);
    	vehicleGroup.add(truckButton);
    	vehicleGroup.add(trainButton);
    	vehicleGroup.add(helicopterButton);
    	vehicleGroup.add(planPathButton);
    	SPFButton = new JRadioButton("Dijkstra SPF");
    	SPFButton.addActionListener(bH);
    	SPFButton.setSelected(true);
    	aStarButton = new JRadioButton("A*");
    	aStarButton.addActionListener(bH);
    	
    	searchGroup = new ButtonGroup();
    	searchGroup.add(SPFButton);
    	searchGroup.add(aStarButton);
    	pathPanel.add(carButton);
    	pathPanel.add(truckButton);
    	pathPanel.add(trainButton);
    	pathPanel.add(helicopterButton);
    	pathPanel.add(planPathButton);
    	pathPanel.add(SPFButton);
    	pathPanel.add(aStarButton);
    	pathPanel.add(goButton);
		
    	goButton.setEnabled(false);
		carButton.setEnabled(false);
		truckButton.setEnabled(false);
		trainButton.setEnabled(false);
		helicopterButton.setEnabled(false);
		planPathButton.setEnabled(false);
		SPFButton.setEnabled(false);
		aStarButton.setEnabled(false);
    	
		ctrlPanel.add(pathPanel);

		selectedReg = regionMap.get("Suburb").get("ABBOTSFORD");
		return ctrlPanel;
    }
    
    /**
     * Builds up a JMenuBar that consists the menu of the frame and return the JMenuBar
     * with items. Each item is listened do its role when selected
     * @return A menu bar that consists of items
     */
    private JMenuBar initMenu()
    {
        JMenuBar menu = new JMenuBar();
        
        {
            //File Menu
            JMenu mFile = new JMenu("File");
            menu.add(mFile);
            {
            	fileSave = new JMenuItem("Save");
            	mFile.add(fileSave);
            	fileSave.addActionListener(mH);
            	
            	saveAsMenu = new JMenu("Save As..");
            	
            	saveAsVictoria = new JMenuItem("Victoria data..");
            	saveAsMenu.add(saveAsVictoria);
            	saveAsVictoria.addActionListener(mH);
            	
            	saveAsSuburb = new JMenuItem("Suburb data..");
            	saveAsMenu.add(saveAsSuburb);
            	saveAsSuburb.addActionListener(mH);
            	
            	saveAsRoad = new JMenuItem("Road data..");
            	saveAsMenu.add(saveAsRoad);
            	saveAsRoad.addActionListener(mH);
            	
            	saveAsRail = new JMenuItem("Rail data..");
            	saveAsMenu.add(saveAsRail);
            	saveAsRail.addActionListener(mH);
            	
            	saveAsOverlay = new JMenuItem("Overlay data..");
            	saveAsMenu.add(saveAsOverlay);
            	saveAsOverlay.addActionListener(mH);
            	
            	loadMenu = new JMenu("Load Data..");
            	
            	loadVictoria = new JMenuItem("Victoria data..");
            	loadMenu.add(loadVictoria);
            	loadVictoria.addActionListener(mH);
            	
            	loadSuburb = new JMenuItem("Suburb data..");
            	loadMenu.add(loadSuburb);
            	loadSuburb.addActionListener(mH);
            	
            	loadRoad = new JMenuItem("Road data..");
            	loadMenu.add(loadRoad);
            	loadRoad.addActionListener(mH);
            	
            	loadRail = new JMenuItem("Rail data..");
            	loadMenu.add(loadRail);
            	loadRail.addActionListener(mH);
            	
            	loadOverlay = new JMenuItem("Overlay data..");
            	loadMenu.add(loadOverlay);
            	loadOverlay.addActionListener(mH);
            	
            	mFile.add(saveAsMenu);
            	mFile.add(loadMenu);
            	mFile.addSeparator();
            	
                fileExit = new JMenuItem("Exit");
                mFile.add(fileExit);
                fileExit.addActionListener(mH);
                //more code for actionlistener to come   
                
            }
            
            //Display Menu
            JMenu mDisplay = new JMenu("Display");
            menu.add(mDisplay);
            {            	
            	dispSuburb = new JCheckBoxMenuItem("Suburb", false);
            	dispSuburb.addItemListener(cH);
            	mDisplay.add(dispSuburb);
            	
            	dispRoad = new JCheckBoxMenuItem("Road", false);
            	dispRoad.addItemListener(cH);
            	mDisplay.add(dispRoad);
            	
            	dispRail = new JCheckBoxMenuItem("Rail", false);
            	dispRail.addItemListener(cH);
            	mDisplay.add(dispRail);
            	
            	mDisplay.addSeparator();
            	
            	dispOverlay = new JCheckBoxMenuItem("Overlay", true);
            	dispOverlay.addItemListener(cH);
            	mDisplay.add(dispOverlay);
            	
            }
            
            //Help Menu
            JMenu mHelp = new JMenu("Help");
            menu.add(mHelp);
            {
                helpAbout = new JMenuItem("About");
                mHelp.add(helpAbout);
                helpAbout.addActionListener(mH);
            }
        }
        return menu;
    }
     
    /**
     * This class handles any action events of the buttons by implementing
     * ActionListener. It detects events and call appropriate methods in draw manager
     * object. It renews the location of overlay points when any change made in the map
     */
    //Handler for Navigation Button
    private class ButtonHandler implements ActionListener
    {
		public void actionPerformed(ActionEvent e)
		{
			if(e.getSource().getClass().getName() == "javax.swing.JButton")
			{
				JButton tmp = (JButton) e.getSource();
				
				if (tmp == upBtn)
					dm.pan(0,50);
				else if (tmp == downBtn)
					dm.pan(0,-50);
				else if (tmp == leftBtn)
					dm.pan(50,0);
				else if (tmp == rightBtn)
					dm.pan(-50,0);
				else if (tmp == resetBtn)
				{
					dm.reset();
					slider.setValue(0);
				}
				else if (tmp == zoomInBtn)
				{
					dm.zoomIn();
					slider.setValue((slider.getValue()+2));
				}
				else if (tmp == zoomOutBtn)
				{
					dm.zoomOut();
					slider.setValue(slider.getValue()-2);
				}
				else if (tmp == goButton)
				{
					dm.makePath();
					dm.resetPath();
					goButton.setEnabled(false);
					
					//for (Entry<String, Region> entry : regionMap.get("Road").entrySet() )
						//entry.getValue().selected = false;
					
					
				}
				renewPoint();
			}
			else if(e.getSource().getClass().getName() == "javax.swing.JRadioButton")
			{
				JRadioButton tmp = (JRadioButton) e.getSource();
				
				if (tmp == suburbJR)
				{
					initCombo("Suburb");
					editCheck.setEnabled(false);
					addCheck.setEnabled(false);
					colorButton.setEnabled(true);
				}
				else if (tmp == roadJR)
				{
					initCombo("Road");
					editCheck.setEnabled(true);
					colorButton.setEnabled(false);
				}
				else if (tmp == railJR)
				{
					initCombo("Rail");
					editCheck.setEnabled(true);
					colorButton.setEnabled(false);
				}
			}
		}
    }
    
    /**
     * This class handles any action events of the check box menu items
     * in the menu bar. It detects whether the item is checked or unchecked and
     * call appropriate method. 
     * @author Heong Jin
     * @author David
     *
     */
    //Handler for checkboxes in Display Menu
    private class CheckboxHandler implements ItemListener
    {
		//when check box selected
		public void itemStateChanged(ItemEvent e)
		{
			JCheckBoxMenuItem tmp = (JCheckBoxMenuItem) e.getSource();
			
			if (e.getStateChange() == 1)
				enableDisplay(tmp);
			else
				disableDisplay(tmp);
		}
    } //End of CheckboxHandler Class
    

    /**
     * This class handles any action events when user select the item
     * from the menu. It detects the event and call appropriate method to
     * process the request.
     * @author Heong Jin
     * @author David
     *
     */
    private class MenuHandler implements ActionListener
    {
        public void actionPerformed(ActionEvent e)
        {
        	JMenuItem tmp = (JMenuItem) e.getSource();
                
            if (tmp == fileSave)
             	doSave();
            else if (tmp == saveAsVictoria)
              	doSaveAs("Victoria");
            else if (tmp == saveAsSuburb)
              	doSaveAs("Suburb");
            else if (tmp == saveAsRoad)
              	doSaveAs("Road");
            else if (tmp == saveAsRail)
              	doSaveAs("Rail");
            else if (tmp == saveAsOverlay)
              	doSaveAs("Overlay");
            else if (tmp == fileExit)
                doExit();
            else if (tmp == helpAbout)
                doAbout();
            else if (tmp == loadVictoria)
              	doLoad("Victoria");
            else if (tmp == loadSuburb)
              	doLoad("Suburb");
            else if (tmp == loadRoad)
              	doLoad("Road");
            else if (tmp == loadRail)
              	doLoad("Rail");
            else if (tmp == loadOverlay)
              	doLoad("Overlay");
            
        }
    } // End of MenuHandler Class

    //Handler for dragging and dropping points
  	/**
  	 * This class handles action events when a label is moved by dragging
  	 * to set the location of the label with the position where mouse gets released.
  	 * @author Heong Jin
  	 * @author David
  	 *
  	 */
    private class pointHandler extends MouseAdapter implements MouseMotionListener 
  	{
  		int xx = 0;
  		int yy = 0;
  		int dx = 0;
  		int dy = 0;
  		pointLabel pL;
  		
  		public pointHandler(pointLabel point)
  		{
  			pL = point;
  		}
  		
  		public void mouseDragged(MouseEvent e)
  		{
  			xx += e.getX()-dx-10;
  			yy += e.getY()-dy-10;
  			
  			Point pixel = new Point();
  			dm.getInverseAT().transform(new Point(xx,yy), pixel);
  			pL.setLocation(xx,yy); //Change Location of current JLabel in affined pixel
  			pL.setAmg(pixel); //Set default AMG location of the Point
  			setState(true);
  		}
  		public void mouseMoved(MouseEvent e) {
  		}
  		public void mousePressed(MouseEvent e)
  		{
  			xx = ((JLabel)(e.getSource())).getX();
  			yy = ((JLabel)(e.getSource())).getY();
  			dx = e.getX();
  			dy = e.getY();
  		}
  		public void mouseReleased(MouseEvent e)
  		{
  			updateOverlay(pL);
  		}
  	}
    
    /**
     * This method allows user to select a scenario file to load
     * 
     */
    private void doLoad(String region)
    {
    	int choice = fChooser.showOpenDialog(this);
    	
    	if(choice == JFileChooser.APPROVE_OPTION)
    	{ //Returned value check
    		File file = fChooser.getSelectedFile(); 
    		
    		try {
    			Properties.setProperty(region, file.getName());
				regionMap = File.loadData(Properties);
			} catch (IOException e) 
			{
				e.printStackTrace();
			}
    		
    		String[] str = file.getName().split("\\.");
    		currentOverlay = str[0];
    		setState(false);
    		drawPoint();
    		dm.updateDraw();
    	}
    }
    
    /**
     * This method lets user exit the program. It asks if the user wants to save
     * current scenario file when there is a change since last load.
     */
    private void doExit()
    {
        boolean b = askSure("exit?");
        if(b)
        {
        	if(getChangedState()) // check if there's change in overlay
        	doSave();
            setVisible(false);
            dispose();
            System.exit(0);
        }
    }
    
    /**
     * This method allows user save the current overlay file.
     */
    private void doSave()
    {    	
    	boolean b = askSure("Save changes?");
    	if(b)
    	{
    		try 
    		{
    			for (Entry<Object, Object> e : Properties.entrySet())
    			{
    				String region = e.getKey().toString();
    				
    				if (region.compareToIgnoreCase("Path") != 0)
    					File.saveData(e.getValue().toString(), regionMap.get(e.getKey()));	
    				
    				File.saveProperties(Properties);
    			}
			} 
    		catch (IOException e) 
    		{
				e.printStackTrace();
			}
    		setState(false);
    	}
    	
    }
    
    /**
     * This method allows user save the current overlay file with a different name
     * in a different folder.
     */
    private void doSaveAs(String region)
    {
    	int ans=fChooser.showSaveDialog(this);
    	
    	if (ans==0)
    	{
    	   File file=fChooser.getSelectedFile();
    	   
    	   try
    	   {
    		   if (file.getName().contains(".txt"))
    		   {
    			   File.saveData(file.getName(), regionMap.get(region));
	    		   Properties.setProperty(region, file.getName());
    		   }
    		   else
    		   {
	    		   File.saveData(file.getName() + ".txt", regionMap.get(region));
	    		   Properties.setProperty(region, file.getName() + ".txt");
    		   }

    		   File.saveProperties(Properties);
    	   }
    	   catch(IOException e1){
    		   e1.printStackTrace();
    	   }
    	   setState(false);
    	}
    }
    
    /**
     * Displays the information of this program.
     */
    private void doAbout()
    {
        JOptionPane.showMessageDialog(this, 
                "<HTML><B>Java Melbourne Map</B> version 2.0</BR>"
                + "<P><BR>Author: <FONT COLOR=\"#0000ff\"><I>7019777 Heong Jin Yu, 7183216 David Zappia</I></FONT></P>"
                + "<P><BR>Date: <FONT COLOR=\"#0000ff\"><I>02 11 2012</I></FONT></P>"
                + "<P><BR>This program is completed version for submission. ADVANCED JAVA ASSIGNMENT 2.</P>", 
                "About", JOptionPane.INFORMATION_MESSAGE);
    }
    
    /**
     * Displays selected region on the map.
     * @param dispItem selected region to display
     */
    private void enableDisplay(JCheckBoxMenuItem dispItem)
    {
    	if (dispItem == dispOverlay)
    	{
    		showPoint(true);
    		renewPoint();
    	}
    	else if (dispItem == dispRail)
    		dm.addRegion(3, regionMap.get("Rail"));
    	else if (dispItem == dispRoad)
    		dm.addRegion(2, regionMap.get("Road"));
    	else if (dispItem == dispSuburb)
    		dm.addRegion(1, regionMap.get("Suburb"));
    }
    
    /**
     * erases selected region from the map
     * @param dispItem selected region to erase
     */
    private void disableDisplay(JCheckBoxMenuItem dispItem)
    {
    	if (dispItem == dispOverlay)
    	{
    		dm.removeRegion(4);
    		showPoint(false);
    	}
    	else if (dispItem == dispRail)
    		dm.removeRegion(3);
    	else if (dispItem == dispRoad)
    		dm.removeRegion(2);
    	else if (dispItem == dispSuburb)
    		dm.removeRegion(1);
    }
    
    /**
     * Displays a pop up window with a passed message for user to confirm
     * or cancel the choice.
     * @param sureFor String that will be displayed on the window
     * @return boolean value selected by user
     */
    private boolean askSure(String sureFor)
    {
        StringBuffer sb = new StringBuffer();
        sb.append("Do you want to ");
        sb.append(sureFor);
        int result = JOptionPane.showConfirmDialog(this,
                sb.toString(), "" , JOptionPane.YES_NO_OPTION,
                JOptionPane.QUESTION_MESSAGE, null);
        sb=null;
        if(result == JOptionPane.YES_NO_OPTION){
            return true;
        }
        else{
            return false;
        }
    } 
    
    /**
     * This method creates a dialog window and displays it 
     * @param x AMG point of x position
     * @param y AMG point of y position
     * @param msg The title of the dialog
     */
    public void doDialog(int x, int y, String msg)
    {
    	String tempTitle = msg + Integer.toString(x)+", "+Integer.toString(y);
    	pD = new pointDialog(x, y, "Input text for the point?");
    	pD.setSize(new Dimension(300, 150));
    	pD.setTitle(tempTitle);
    	pD.setLocationRelativeTo(this);
        pD.setVisible(true);
    }

	/**
	 * This class provides a user a dialog window where the user can write his message
	 * for a certain point.  
	 * This consists of text field where user types in and buttons for deployment.
	 */
	class pointDialog extends JDialog implements ActionListener, PropertyChangeListener
	{
		private String typedText = null;
		private JTextField textField;
		
		private JOptionPane optionPane;
		
		private String btnString1 = "Enter";
	    private String btnString2 = "Cancel";
	    
	    private int xPos;
	    private int yPos;
	    
	    boolean isAdd;
	    private pointLabel pL = null;
	    
		public pointDialog(int x, int y, String msg)
		{
			xPos = x;
			yPos = y;
			
			textField = new JTextField(20);
			//Create an array of the text and components to be displayed.
	        Object[] array = {msg, textField};
	        Object[] options = {btnString1, btnString2};
			
	        optionPane = new JOptionPane(array,
	        		JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION,
	        		null, options, options[0]);
	        setContentPane(optionPane);
	        
	        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
	        
	        addWindowListener(new WindowAdapter() 
	        {
	        	public void windowClosing(WindowEvent we) //Instead of directly closing the window, the JOptionPane's value property gets changed 
	        	{
	        		optionPane.setValue(new Integer(JOptionPane.CLOSED_OPTION));
	            }
	        });

	        //Ensure the text field always gets the first focus.
	        addComponentListener(new ComponentAdapter() {
	            public void componentShown(ComponentEvent ce) {
	                textField.requestFocusInWindow();
	            }
	        });
	        //Register an event handler that reacts to option pane state changes.
	        optionPane.addPropertyChangeListener(this);          
		}// End Constructor
		
		public pointDialog(int x, int y, pointLabel pLtmp)
		{
			pL = pLtmp;
			xPos = x;
			yPos = y;
			
			textField = new JTextField(20);
			//Create an array of the text and components to be displayed.
	        Object[] array = {"Input text for the point?", textField};
	        Object[] options = {btnString1, btnString2};
			
	        optionPane = new JOptionPane(array,
	        		JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION,
	        		null, options, options[0]);
	        setContentPane(optionPane);
	        
	        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
	        
	        addWindowListener(new WindowAdapter() 
	        {
	        	public void windowClosing(WindowEvent we) //Instead of directly closing the window, the JOptionPane's value property gets changed 
	        	{
	        		optionPane.setValue(new Integer(JOptionPane.CLOSED_OPTION));
	            }
	        });

	        //Ensure the text field always gets the first focus.
	        addComponentListener(new ComponentAdapter() {
	            public void componentShown(ComponentEvent ce) {
	                textField.requestFocusInWindow();
	            }
	        });
	        //Register an event handler that reacts to option pane state changes.
	        optionPane.addPropertyChangeListener(this);          
		}// End Constructor

		/**
		 * This method clears the dialog and hides it.
		 */
	    public void clearAndHide() 
	    {
	        textField.setText(null);
	        setVisible(false);
	    }
	    
	    // This method handles events for the text field.
	    public void actionPerformed(ActionEvent e) 
	    {
	        optionPane.setValue(btnString1);
	    }
	    
		// This method reacts to state changes in the option pane.
	    public void propertyChange(PropertyChangeEvent e) 
	    {
	        String prop = e.getPropertyName();

	        if (isVisible() && (e.getSource() == optionPane) && (JOptionPane.VALUE_PROPERTY.equals(prop) ||	JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) 
	        {
	        	Object value = optionPane.getValue();

	            if (value == JOptionPane.UNINITIALIZED_VALUE) 
	            {
	                //ignore reset
	                return;
	            }

	            //Reset the JOptionPane's value.
	            //If you don't do this, then if the user
	            //presses the same button next time, no
	            //property change event will be fired.
	            optionPane.setValue(
	                    JOptionPane.UNINITIALIZED_VALUE);

	            if (btnString1.equals(value)) 
	            { //user select enter with text
	            	typedText = textField.getText();
	            	if(pL==null)
	            		doAddPoint(xPos, yPos, typedText);
	            	else
	            		pL.setText(typedText);

	                clearAndHide();
	            } 
	            else //user closed dialog or clicked cancel 
	            { 
	                typedText = null;
	                clearAndHide();
	            }
	        }
	    }
	    
	    /**
	     * This method returns the value in the text filed of the dialog
	     * @return the typed text
	     */
	    public String getTypedText()
	    {
	    	return typedText;
	    }
	}//End of pointDialog
	
	/**
	 * This methods creates a point based on the information passed. Then it calls
	 * another method to add a label on the map
	 * @param x AMG point of x position
	 * @param y AMG point of y position
	 * @param txt the text for label
	 */
	public void doAddPoint(int x, int y, String txt)
	{
		Node entry = new Node(0, x, y);
		ArrayList<Node> list = new ArrayList<Node>();
		list.add(entry);
		Region r = new Region(txt, list, -1);

		regionMap.get("Overlay").put(txt, r); // Create new entry for the key

		addLabel(txt, entry);
		dm.updateDraw();
		
		setState(true);
	}
    
	/**
	 * This method returns the list of regions
	 * @return the regions
	 */
	public Map<String, Map<String, Region>> getRegionMap()
	{
		return regionMap;
	}
	
	/**
	 * This method returns the draw manager
	 * @return the draw manager
	 */
	public DrawManager getDrawManager()
	{
		return dm;
	}	
	
	/**
	 * This method stores all of points stored in the current overlay
	 * in a label array
	 */
	public void drawPoint()
	{
		Map<String, Region> overlayMap = regionMap.get("Overlay"); //temp map
		
		removeLabel();
		pointList.clear();
		
		Iterator it = overlayMap.keySet().iterator();
		while (it.hasNext())
		{
			String key = it.next().toString();
			addLabel(key, overlayMap.get(key).getData().get(0)); // Get the first line(only) of the overlay key
		}
	}
	
	/**
	 * This method removes all of labels exist on the map
	 */
	public void removeLabel()
	{
		Iterator<pointLabel> it = pointList.iterator();
		pointLabel pLtmp;
		
		while(it.hasNext())
		{
			pLtmp = it.next();
			dm.getDrawPanel().remove(pLtmp);
		}
	}
	
	/**
	 * This method synchronises the label array with the current overlay.
	 * Overwrites current point if the key is same
	 * @param pL The point label
	 */
	public void updateOverlay(pointLabel pL) // update regionMap of Overlay to sync
	{
		ArrayList<Point2D> entry = new ArrayList<Point2D>();
		
		entry.add(pL.getAmg());
		//regionMap.get("Overlay").getData().put(pL.getText(), entry); // Overwrite existing Array
	}
	
	/**
	 * This method creates a label for the passed point and add it on the map panel
	 * It renews the entire labels on the map every time it is called then update
	 * the overlay with the point label.
	 * It removes the current label if there is a label with same text
	 * @param txt the text of the label
	 * @param newPoint the Point2D value
	 */
	public void addLabel(String txt, Point2D newPoint)
	{
		boolean noDup = true;
		Iterator<pointLabel> it = pointList.iterator();
		String tmp;
		pointLabel pLtmp;
		
		while (it.hasNext()) // to check if new is duplicate.
		{
			pLtmp = it.next();
			tmp = pLtmp.getText();
			if(tmp.equals(txt))
			{
				dm.getDrawPanel().remove(pLtmp);
				noDup = false;
			}
		}

		pointLabel pL = new pointLabel(txt);
		
		pL.setAmg(newPoint);
		pL.addMouseMotionListener(new pointHandler(pL));
		pL.addMouseListener(new pointHandler(pL));
		
		pL.setSize(txt.length()*10, 13);
		dm.getDrawPanel().add(pL);
		pointList.add(pL);
		
		renewPoint();
		updateOverlay(pL);
	}
	
	/**
	 * This method renews all existing points in the list with the current
	 * affine transformation then relocate them.
	 */
	public void renewPoint() // get point2d from existing pointList and change their location with the current Affine Transformation
	{
		Iterator<pointLabel> it = pointList.iterator();
		Point pixel = new Point();
		
		while (it.hasNext())
		{
			pointLabel pL = it.next();
			dm.getAT().transform(pL.getAmg(), pixel);
			pL.setLocation(new Point(pixel.x, pixel.y));
		}
	}
	
	/**
	 * this method displays or hides all point labels in the list.
	 * @param b the boolean value
	 */
	public void showPoint(boolean b)
	{
		Iterator<pointLabel> it = pointList.iterator();
		
		while(it.hasNext())
		{
			pointLabel pL = it.next();
			pL.setVisible(b);
		}
	}
	
	/**
	 * This class is a JLabel that contains Point2D value of AMG point.
	 * It adds itself a transfer handler for handling mouse dragging 
	 * to move the label. And it enables user to edit the current label
	 * where the mouse pointer clicked on
	 * @author Heong Jin
	 * @author David
	 */
	private class pointLabel extends JLabel
	{
		private Point2D amgP; //Point information of default location (in amg)
		pointLabel(String txt)
		{
			setTransferHandler(new TransferHandler(txt));
			setText(txt);
			addMouseListener(new MouseAdapter()
			{
				public void mousePressed(MouseEvent evt)
				{
					JComponent comp = (JComponent)evt.getSource();
					TransferHandler th = comp.getTransferHandler();
					
					// Start the drag operation
					th.exportAsDrag(comp, evt, TransferHandler.COPY);
				}
				public void mouseClicked(MouseEvent evt)
				{
					
					int x = (int)amgP.getX();
					int y = (int)amgP.getY();
					
			        String tempTitle = "Edit point at" + Integer.toString(x)+", "+Integer.toString(y);
			    	pD = new pointDialog(x, y, pointLabel.this);
			    	pD.setSize(new Dimension(300, 150));
			    	pD.setTitle(tempTitle);
			    	pD.setLocationRelativeTo(MelbourneMap.this);
			        pD.setVisible(true);
				}
			});
		}
		
		/**
		 * This method returns the AMG point of its current position
		 * @return Point2D AMG
		 */
		public Point2D getAmg()
		{
			return amgP;
		}
		
		/**
		 * This method sets the AMG point with the passed {@link Point2D} value.
		 * @param newAmg Point2D value of the AMG
		 */
		public void setAmg(Point2D newAmg)
		{
			amgP = newAmg;
		}
	}
	
	/**
	 * This method change the value of changedState.
	 * @param b The boolean value
	 */
	public void setState(boolean b)
	{
		changedState = b;
	}
	/**
	 * This method returns the value of changedState.
	 * @return The boolean value of changedState
	 */
	public boolean getChangedState()
	{
		return changedState;
	}
	
	/**
     * Initialises the combobox with the selected region type that is listened from radio buttons
     * @param sureFor String that will be displayed on the window
     * @return boolean value selected by user
     */
	public void initCombo(String regionType)
	{
		regionCombo.removeAllItems();

		Set keys = regionMap.get(regionType).keySet();
		String[] sortedKeys = (String[]) keys.toArray(new String[keys.size()]);
		Arrays.sort(sortedKeys);
		
		for (int i= 0; i < sortedKeys.length; i++)
			regionCombo.addItem(sortedKeys[i]);
		
	}
	
	public void selectRegion(String regionName)
	{
		
		Region tmpReg = null;
		
		if(suburbJR.isSelected())
		{
			tmpReg = regionMap.get("Suburb").get(regionName);
		}
		else if(roadJR.isSelected())
			tmpReg = regionMap.get("Road").get(regionName);
		else if(railJR.isSelected())
			tmpReg = regionMap.get("Rail").get(regionName);
		if(tmpReg!=null)
		{
			selectedReg.selected = false;
			for(Node n : selectedReg.getData())
				n.editable = false;
			
			selectedReg = tmpReg;
			selectedReg.selected = true;
			
			for(Node n : selectedReg.getData())
				n.editable = true;
			
			if(tmpReg.getColor()==null)
				tmpReg.setColor(new Color(0,255,255));
			tmpReg.printData();
			dm.updateDraw();
		}
		
	}

	//For ComboBox
	public void itemStateChanged(ItemEvent e) 
	{
		if(e.getItem() != null)
        	selectRegion(e.getItem().toString());
	}

	//For Slider zooming
	public void stateChanged(ChangeEvent e) 
	{
		JSlider source = (JSlider)e.getSource();
		if(!source.getValueIsAdjusting())
		{
			int fps = (int)source.getValue();
			dm.zoom(fps);
			renewPoint();
		}	
	}

	
}
